

package net.matvec;


public class Matrix {

    protected double[][] data;
    protected int rows;
    protected int columns;

    static {
	Runtime.getRuntime().load("/usr/local/lib/libjnimatvec.so");
    }

    private static native double[] vm_read_matrix(String file, Matrix m);
    private native void vm_multiply(double[] m1, int m1d, double[] m2,
	int m2d, double[] res) throws InvalidMatrixOperandsException;
    private native void vm_transpose(double[] m, int md, double[] res);
    private native double vm_determinant(double[] m, int md)
	throws InvalidMatrixOperandsException;
    private native void vm_inverse(double[] m, double[] res);
    private native void vm_minor_matrix(
	double[] m, int md, int r, int c, double[] res);
    private native double vm_cofactor(double[] m, int md, int r, int c);
    private native void vm_adjoint(double[] m, int md, double[] res);
    private native void vm_print_matrix(double[] m, int md, String pre);

    public Matrix(){
    }

    public Matrix(double[][] data){
	this.data = data;
	this.rows = data.length;
	this.columns = data[0].length;
    }

    public Matrix(double[] data, int rows) 
	throws BadMatrixDataException {
	
	int cols = data.length / rows;
	if ((cols * rows) != data.length){
	    throw new BadMatrixDataException("rows value does not work " +
		"with data length: rows=" + rows);
	}

	this.setMatrixData(data, rows);

    }

    public Matrix(int rows, int cols){
	this(new double[rows][cols]);
    }

    public double[] toDoubleArray(){
	double[] oneDData = new double[rows * columns];
	//System.out.printf("[Matrix.toDoubleArray] rows=%d cols=%d\n",
			  //rows, columns);

	int offset = 0;
	for (int i = 0; i < rows; i++){
	    for (int j = 0; j < columns; j++){
		oneDData[offset++] = data[i][j];
	    }
	}
	return oneDData;
    }

    public void setMatrixData(double[] data, int rowLen){
	
	int offset = 0;
	int cols = data.length / rowLen;
	//System.out.println("Rowlen=" + rowLen + " Collen=" + cols);
	this.data = new double[rowLen][cols];
	this.rows = rowLen;
	this.columns = cols;
	for (int i = 0; i < rowLen; i++){
	    for (int j = 0; j < cols; j++){
		this.data[i][j] = data[offset++];
	    }
	}

    }

    public void setElement(int row, int col, double data) 
	throws ArrayIndexOutOfBoundsException {
	this.data[row][col] = data;
    }

    public double getElement(int row, int col) 
	throws ArrayIndexOutOfBoundsException {
	return this.data[row][col];
    }

    public static Matrix readMatrix(String file) throws MatrixIOException {
	Matrix m = new Matrix();
	double[] mData = vm_read_matrix(file, m);
	if (mData == null){
	    throw new MatrixIOException("Unable to read matrix.");
	}
	m.setMatrixData(mData, m.rows);
	return m;
    }

    public void multiply(Matrix m1, Matrix m2, Matrix res) 
	throws InvalidMatrixOperandsException {
	double[] m1d = m1.toDoubleArray();
	double[] m2d = m2.toDoubleArray();
	double[] resm = new double[m1.rows * m2.columns];
	vm_multiply(m1d, m1.rows, m2d, m2.rows, resm);
	res.setMatrixData(resm, m1.rows);
    }

    public void print(String prepend){
	vm_print_matrix(this.toDoubleArray(), this.rows, prepend);
    }

    public int getRows(){
	return rows;
    }

    public int getColumns(){
	return columns;
    }

}
